package Study2;

import java.util.Arrays;
import java.util.Scanner;

/**
 * 异常探究
 */

class Person10 implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


class Test {
    public int num = 100;


}

/**
 * 自定义异常类
 */
/*
自定义异常通常要继承 Exception 或者 RuntimeException 继承自 Exception 的异常默认是受查异常(编译出错，无法通过)
继承自 RuntimeException 的异常默认是非受查异常(运行出错)
 */

class Demo extends RuntimeException {
    /*
    所谓的自定义异常类，就是和 throw new 抛出异常结合起来，调用自定异常的类名，并处理
     */
    public Demo() {
        super();
    }

    public Demo(String message) {
        super(message);
    }
}



public class Study25 {
    /**
     * 异常处理
     */
    public static void func2(int y) throws Demo{
        if( 10 == y) {
            throw new Demo("自定义异常");
        }

    }
    public static void main(String[] args) {

        try {    // 抛出可能出现的代码，方法，操作
            func2(10);
        }catch (Demo e) {   // 捕获 catch 抛出 try 并处理
            e.printStackTrace();   // 爆红，提醒代码出现异常的位置 ，也可以省略掉的
            System.out.println("自定义异常处理");
        }

        System.out.println("hello world");
    }

    public static void main23(String[] args) {
        int y = 10;
        if( 10 == y) {
            throw new Demo("自定义异常");
        }
        System.out.println("hello world");

    }


    public static void main21(String[] args) throws CloneNotSupportedException {
        Person10 person10 = new Person10();
        Person10 person101 = (Person10) person10.clone();
        System.out.println("hello world");
    }
    public static void main22(String[] args) {
        Person10 person10 = new Person10();

        try {
            Person10 person101 = (Person10)person10.clone();
        }catch (CloneNotSupportedException e) {
            e.printStackTrace();
            System.out.println("捕获到 clone 的异常");
        }
        System.out.println("hello world");

    }


    /*
    创建异常对象 throw
    在抛出自定义异常时，我们可以根据实际情况来抛出需要的异常，在构造异常对象同时可以指定一些描述性信息
    我们在处理异常的时候，通常希望知道这段代码中究竟会出现那些可能的异常
    我们可以使用 throws 关键字，把可能抛出的异常显示的标注在方法定义的位置，从而提醒调用着注意捕获
    这些异常，
     */
    public static int divide(int x ,int y) throws ArithmeticException{  // throws 异常的声明
        if(0 == y) {
            throw new ArithmeticException("y == 0");  // 根据条件抛出异常
        }
        return x / y;
    }
    public static void main18(String[] args) {
        try {
            int ret = divide(20,10);
            ret = divide(10,0);
            System.out.println(ret);
        } catch (ArithmeticException e) {
            e.printStackTrace(); // 爆红，提醒代码出现异常的位置
            System.out.println("捕获到算术异常");
        }
        System.out.println("hello world");
    }


    /*
    如果本方法中没有合适 处理异常单独方式，就会沿着调用栈(调用者）向上传递，找到处理异常的方法
    ，如果向上一直传递都没有合适的方法处理异常，最终就会交给JVM处理，程序就会异常终止
     */
    public static void func2() {
        int[] arr = {1,2,3};
        System.out.println(arr[100]);
    }
    public static void main20(String[] args) {
        func2();   // 没有经过异常处理的异常，编译通过交给JVM虚拟机运行处理，报错
        System.out.println("hello world");
    }

    public static void main19(String[] args) {
        try {
            func2();
        }catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
            System.out.println("捕获到数组越界异常");
        }

        System.out.println("hello world");
    }



    /*
    finally 表示最后的善后工作，例如释放内存空间，资源
    无论是否存在异常的出现，finally都是一定会被执行到的
    而且 finally 只能有一个在 try 中
    不建议在 finally 中出现 return 该返回无论是否存在异常都是被优先返回的，建议使用在关闭资源，善后上
     */
    public static void main16(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = 100;

        try {   // 抛出可能出现的异常代码，方法，操作
            n = scanner.nextInt();
            System.out.println(10 / n);
        } catch(ArithmeticException e) {  // catch捕获到 try抛出的异常，并处理该异常类
            e.printStackTrace();   // 爆红，提醒代码出现的异常位置
            System.out.println("算术异常被除数不可以为 0");
        } finally {               // finally 无论是否存在异常都会被执行到，关闭资源，善后工作
            scanner.close();  // 关闭IO输入流
        }
        System.out.println("hello world");

    } // 下面爆黄，优化的
    public static void main17(String[] args) {
        // 在 try 中可以是可以附加上括号 创建对象的，调用构造方法的
        try (Scanner scanner = new Scanner(System.in)) {   // 抛出可能出现异常的代码，方法，操作
            int n = 100;
            n = scanner.nextInt();
            System.out.println(10 / n);
        } catch (ArithmeticException e) {  // catch 捕获到 try 中抛出的对应的异常，并处理异常类
            e.printStackTrace();      // 爆红，提醒代码出现异常的位置所在
            System.out.println("算术异常被除数不可以为 0");
        } // 优化把 finally 去了
        // 关闭IO输入流
        System.out.println("hello world");
    }



    public static int func(int i) {
        int[] array = {1,2,3};
        try {
            System.out.println(array[5]);
            System.out.println("不会被执行到");
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace(); // 爆红，提醒代码出现异常的位置
            return 0;
        } finally {
            System.out.println("无论是否存在异常都会被执行到");
            return 2;
        }
    }

    public static void main15(String[] args) {
        int ret = func(2);
        System.out.println(ret);
        /*
        在存在异常的情况下 catch中的 retrun返回值，比 finally 中的 return 满
         */
    }

    public static int func(String str) {
        int[] array = {1,2,3};

        try {
            System.out.println(array[2]);
            return 0;
        } catch(ArrayIndexOutOfBoundsException e) {
            e.printStackTrace(); // 爆红，提示代码出现错误的位置
            System.out.println("捕获到数组越界的异常");
        } finally {
            System.out.println("无论是否存在异常都会被执行到");
            return 2;
        }
    }

    public static void main14(String[] args) {
        int ret = func("1");
        System.out.println(ret);
        /*
        finally 在 try 没有异常的情况下，finally return 是被优先返回的
         */
    }

    public static void main13(String[] args) {
        int[] array = {1,2,3};

        try {
            System.out.println(array[5]);
            System.out.println(array[2]);
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
            System.out.println("捕获到数组越界异常");
        } finally {
            System.out.println("finally块的代码无论是否存在异常夺回被执行到的，用于善后工作");
        }
    }



public static void main12(String[] args) {
    int[] array = {1,2,3};

    try {  // 抛出可能出现的异常的代码，方法，操作
        System.out.println(array[5]);  // 已经出现了异常情况，其中后面的代码是不会执行到的，而后面经过捕获catch处理的可以运行
        System.out.println("不会被执行到的");
    } catch (ArrayIndexOutOfBoundsException e) {  // catch 捕获到 try 抛出的异常并处理
        e.printStackTrace();
        System.out.println("捕获到数组越界异常");
    }

    System.out.println("hello world");
}




    /*
      catch捕捉父类异常Exception,在最开头的话，
      后面的异常都会失效的，因为只要是异常，该父类异常在最开头，
      都会被捕获到，后面根本上就无法执行，捕获到异常
     */
    public static void main11 (String[] args) {
        int[] array = {1,2,3};
        Test test = null;

        try {  // 抛出可能出现异常的代码，方法，操作
            System.out.println(array[5]);
            System.out.println(test.num);
        } catch (Exception e) {    // 异常父类 Exception 捕获到 try 抛出的所有异常情况
            e.printStackTrace();   // 爆红，提示出现异常代码的位置
            System.out.println("异常父类 Exception 捕获到");
        }/* catch (ArrayIndexOutOfBoundsException e) {  // catch 捕获到 try 抛出的对应的异常，并处理
            e.printStackTrace();   // 爆红，提示出现异常代码的所在位置
            System.out.println("捕获到数组越界异常");
        } catch (NullPointerException e) {   // catch 捕获 try 抛出的对应的异常，并处理
            e.printStackTrace();    // 爆红，提示出现异常代码的位置
            System.out.println("捕获到空null 引用访问异常");
        }*/
        System.out.println("hello world");
    }


    /*
    可以用捕获(catch)异常的父类Exception 捕获(catch) 所有的异常情况
    但是不建议使用，因为这样的话，异常的父类Exception 中存在那么多的子类异常，
    你根本无法确定是其中子类中的哪一个子类异常
    用于 Exception 类是所有异常类的父类，因此可以用这个类型表示捕捉所有异常
备注：catch 进行类型匹配的时候，不光会匹配相同类型的
异常对象，也会捕捉目标异常类型的子类对象都是 Exception 的子类，因此都能被捕捉到

     */
    public static void main10(String[] args) {
        int[] array = {1,2,3};
        Test test = null;

        try {    // 抛出可能出现异常的方法，代码，操作
            System.out.println(test.num);
            System.out.println(array[5]);
        } catch (Exception e) {     // Exception e 异常的父类，捕获到try 中的对应的异常并处理，这里是
            // Exception 不建议使用该异常父类，因为你无法确定是那个异常类出现的
            e.printStackTrace();   // 爆红，提出出现的异常代码的所在位置
            System.out.println("捕获到异常");
        }

        System.out.println("hello world");
    }




    /*
    在 catch 中可以使用 | 或的方式但是不建议，
    因为你无法在其中的选择上，判断具体是那个异常
     */
    public static void main9(String[] args) {
        int[] array = {1,2,3};
        Test test = null;

        try {   // 抛出可能出现异常的代码，方法 ，操作
            System.out.println(array[2]);
            System.out.println(test.num);
        } catch (ArrayIndexOutOfBoundsException | NullPointerException e) {  // 捕获到 try 中对应的异常类，并处理，可以使用|
            // 注意，其中的代指变量e， 只能存在一个，而且必须是在最后面的一个，不让语法上无法通过
            e.printStackTrace();  // 爆红，提示出现异常情况的代码的所在位置
            System.out.println("数组越界异常或是 空null 引用访问异常");
        }

        System.out.println("hello world");
    }



    /*
    catch 可以存在多个
     */
    public static void main8(String[] args) {
        int[] array = {1,2,3};

        Test test = null;

        try {  // 抛出可能出现的 代码，方法调用，操作中的异常情况
            System.out.println(array[5]);
            System.out.println(test.num);
        } catch (ArrayIndexOutOfBoundsException e) {   // 捕获到 try 中抛出的异常，并处理
            // catch 可以多个使用
            e.printStackTrace();   // 爆红，提示异常操作出现的位置
            System.out.println("数组越界异常");
        } catch (NullPointerException e) {            // 捕获到 try 中抛出的异常，并处理
            e.printStackTrace();   // 爆红，提示异常操作出现的位置
            System.out.println("空null 类的调用访问异常");
        }

        System.out.println("hello world");


    }

    public static void main7(String[] args) {
        final int n = 99;
//        n = 100;  // 这是错误，不是异常，编译无法通过，语法上的错误
    }

    public static void main6(String[] args) {
        int[] array = {1,2,3};

        try {  // 抛出可能会出现异常的代码，方法，操作
            System.out.println(array[5]);

        }catch (NullPointerException e) {  // 捕获 try 中对应可能出现的异常类
            /*
            注意如果：捕获到的异常于 try 抛出的异常是不匹配的是
            不会执行，捕获的，只会捕获到相对应的异常类，无法捕获到不对应的异常类，
            也就无法处理异常情况，从而导致中止异常
             */
            e.printStackTrace();
            System.out.println("捕获到空null 引用的访问异常");
        }

        System.out.println("hello world");
    }

    public static void main5(String[] args) {
        int[] array = {1,2,3};

        System.out.println(array[5]); // 异常未处理，编译上不会报错，交给 JVM处理，运行错误，中止程序
        System.out.println("hello world");
    }


    public static void main4(String[] args) {
        int[] array = {1,2,3};

        try{  // 抛出的异常
            System.out.println(array[5]);
        }catch (ArrayIndexOutOfBoundsException e){ //捕获 try 抛出的异常类，并处理
            e.printStackTrace();    // 爆红，提示出现异常的所在位置，的代码位置以及该异常信息(该异常的所在路径，异常名)，从而实现解决
            System.out.println(e.toString());// 打印异常存放的位置信息，可以简写成System.out.println(e);
            System.out.println(e);

        }
        System.out.println("hello world");
    }



    public static void main3(String[] args) {
        int[] array = {1,2,3};

        /* 注意：try 和 catch 是相互链接在一起的不可以分开单独使用的
         */
        try{
            // 抛出异常，存放可以出现异常的代码，方法，操作
            System.out.println(array[3]);
        } catch (ArrayIndexOutOfBoundsException e) {  // 捕获到抛出的异常，处理该异常，避免异常的影响
            System.out.println(e);   // e 代表这个异常的信息(该异常类所存放的路径的位置所在)吧，一定意义上的引用该异常类
            System.out.println(e+"捕获到了数组越界异常");
        }
        System.out.println("hello");
    }


    public static void main2(String[] args) {
        int[] array = {1,2,3};
        System.out.println(array[3]);   // 数组越界异常
        System.out.println("hello");
    }


    /*
    所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制 ，关键字: “运行时”
    有些错误是这样的，例如将 System.out.println()拼写错了，写成 System.out.printrr此时在编译上
    (检查语法，过程中就会出错)，编译期间就报错了，而运行时指的是程序已经编译通过了，得到 class 字节码文件了，
    再由JVM执行过程中出现的错误(说白了就是，编译语法上通过，但是再交给JVM虚拟机进行时出错了，无法正常运行，运行中止)
    Throwable 派生出两个重要的子类，Error 和 Exception 其中Error 指的是 Java运行时内部错误，和资源耗尽错误
    应用程序不抛出此类异常，这种内部错误一旦出现，除了告知用户并使程序中止之外，再无能力，这种情况很少出现
    Excption 是我们程序员所使用的异常类的父类
    其中Exception 有一个子类称为 RuntimeExeption IndexoutofBoundSExerption等
    Java语言规范将派生于 Error 类或Rentime Exception类的所有异常称为 非受查异常，所有其他异常称为受查异常
    如果一段代码可能抛出：受查异常，那么必须显示进行过程
     */



    public static void func() {
        func();
    }
    public static void main1(String[] args) {
//        System.out.println(10/0);   算术异常，分母不能为 0

        int[] arr = {1,2,3};
//        System.out.println(arr[4]);  数组越界异常
        Test test = null;
//        System.out.println(test.num);   空(null) 访问异常

//        func();   //压栈异常
    }
    
}
